本文以一型一密预注册的Java代码为例,介绍基于HTTPS通信协议的设备,如何进行动态注册并获取接入物联网平台认证需要的DeviceSecret。
前提条件
已完成一型一密文档中的以下步骤:
- 创建产品。
- 开启动态注册。
- 添加设备。
- 产线烧录。
背景信息
物联网平台支持多种设备安全认证方式,具体认证方式,请参见设备安全认证。
物联网平台支持基于HTTPS通道实现一型一密的预注册认证。相关Topic和参数说明,请参见直连设备的HTTPS动态注册。
准备开发环境
本示例使用的开发环境如下:
- 操作系统:Windows 10
- JDK版本:JDK8
- 集成开发环境:IntelliJ IDEA社区版
操作步骤
- 打开IntelliJ IDEA,创建一个Maven工程。例如HTTPS动态注册。
- 在工程中的pom.xml文件中,添加Maven依赖,然后单击Load Maven Changes图标,完成依赖包下载。
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.83</version> </dependency>
- 在工程HTTPS动态注册的路径/src/main/java下,创建Java类。例如DynamicRegisterByHttps.java,输入以下代码。
说明
- 设备未激活时,可进行多次动态注册,设备的DeviceSecret以最后一次为准。请确保固化到设备的DeviceSecret为最新。
- 设备已激活时,您需调用ResetThing接口重置云端设备动态注册状态为未注册,才能再次动态注册该设备。
import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.Random; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import javax.net.ssl.HttpsURLConnection; import com.alibaba.fastjson.JSONObject; /** * 设备动态注册。 */ public class DynamicRegisterByHttps { // 地域ID,填写您的产品所在地域ID。 private static String regionId = "cn-shanghai"; // 定义加密方式。可选MAC算法:HmacMD5、HmacSHA1、HmacSHA256,需和signmethod一致。 private static final String HMAC_ALGORITHM = "hmacsha1"; /** * 动态注册。 * * @param productKey 产品key * @param productSecret 产品密钥 * @param deviceName 设备名称 * @throws Exception */ public void register(String productKey, String productSecret, String deviceName) throws Exception { // 请求地址。 URL url = new URL("https://iot-auth." + regionId + ".aliyuncs.com/auth/register/device"); HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); conn.setRequestMethod("POST"); conn.setRequestProperty("Content-type", "application/x-www-form-urlencoded"); conn.setDoOutput(true); conn.setDoInput(true); // 获取URLConnection对象对应的输出流。 PrintWriter out = new PrintWriter(conn.getOutputStream()); // 发送请求参数。 out.print(registerdBody(productKey, productSecret, deviceName)); // flush输出流的缓冲。 out.flush(); // 获取URLConnection对象对应的输入流。 BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream())); // 读取URL的响应。 String result = ""; String line = ""; while ((line = in.readLine()) != null) { result += line; } System.out.println("----- register result -----"); System.out.println(result); // 关闭输入输出流。 in.close(); out.close(); conn.disconnect(); } /** * 生成动态注册请求内容。 * * @param productKey 产品ProductKey * @param productSecret 产品密钥 * @param deviceName 设备名称 * @return 动态注册payload */ private String registerdBody(String productKey, String productSecret, String deviceName) { // 获取随机值。 Random r = new Random(); int random = r.nextInt(1000000); // 动态注册参数。 JSONObject params = new JSONObject(); params.put("productKey", productKey); params.put("deviceName", deviceName); params.put("random", random); params.put("signMethod", HMAC_ALGORITHM); params.put("sign", sign(params, productSecret)); // 拼接payload。 StringBuffer payload = new StringBuffer(); for (String key : params.keySet()) { payload.append(key); payload.append("="); payload.append(params.getString(key)); payload.append("&"); } payload.deleteCharAt(payload.length() - 1); System.out.println("----- register payload -----"); System.out.println(payload); return payload.toString(); } /** * 动态注册签名。 * * @param params 签名参数 * @param productSecret 产品密钥 * @return 签名十六进制字符串 */ private String sign(JSONObject params, String productSecret) { // 请求参数按字典顺序排序。 Set<String> keys = getSortedKeys(params); // sign、signMethod除外 keys.remove("sign"); keys.remove("signMethod"); // 组装签名明文。 StringBuffer content = new StringBuffer(); for (String key : keys) { content.append(key); content.append(params.getString(key)); } // 计算签名。 String sign = encrypt(content.toString(), productSecret); System.out.println("sign content=" + content); System.out.println("sign result=" + sign); return sign; } /** * 获取JSON对象排序后的key集合。 * * @param json 需要排序的JSON对象 * @return 排序后的key集合 */ private Set<String> getSortedKeys(JSONObject json) { SortedMap<String, String> map = new TreeMap<String, String>(); for (String key : json.keySet()) { String value = json.getString(key); map.put(key, value); } return map.keySet(); } /** * 使用HMAC_ALGORITHM加密。 * * @param content 明文 * @param secret 密钥 * @return 密文 */ private String encrypt(String content, String secret) { try { byte[] text = content.getBytes(StandardCharsets.UTF_8); byte[] key = secret.getBytes(StandardCharsets.UTF_8); SecretKeySpec secretKey = new SecretKeySpec(key, HMAC_ALGORITHM); Mac mac = Mac.getInstance(secretKey.getAlgorithm()); mac.init(secretKey); return byte2hex(mac.doFinal(text)); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 二进制转十六进制字符串。 * * @param b 二进制数组 * @return 十六进制字符串 */ private String byte2hex(byte[] b) { StringBuffer sb = new StringBuffer(); for (int n = 0; b != null && n < b.length; n++) { String stmp = Integer.toHexString(b[n] & 0XFF); if (stmp.length() == 1) { sb.append('0'); } sb.append(stmp); } return sb.toString().toUpperCase(); } public static void main(String[] args) throws Exception { String productKey = "a1IoK******"; String productSecret = "6vEu5Qlj5S******"; String deviceName = "OvenDevice01"; // 进行动态注册。 DynamicRegisterByHttps client = new DynamicRegisterByHttps(); client.register(productKey, productSecret, deviceName); // 动态注册成功后,需要固化deviceSecret。 } }
- 在以上代码中配置实际设备相关参数。
参数 示例 说明 regionId cn-shanghai 您的物联网平台服务所在地域ID。地域代码表达方法,请参见地域列表。 productKey a1IoK****** 已烧录至设备的产品ProductKey,可登录物联网平台控制台,在产品详情页查看。 productSecret 6vEu5Qlj5S****** 已烧录至设备的产品ProductSecret,可登录物联网平台控制台,在产品详情页查看。 deviceName OvenDevice01 您设备的名称。 因设备激活时会校验DeviceName,建议您采用可以直接从设备中读取到的ID,如设备的MAC地址、IMEI或SN码等,作为DeviceName使用。
- 运行程序文件DynamicRegisterByHttps.java,使设备携带DeviceName和所属产品的ProductKey、ProductSecret向云端发起认证请求。执行结果如图所示,物联网平台校验通过后,设备接收到云端下发的DeviceSecret(
6b14088fa377e8f852d82f7f********
)。
后续步骤
设备获得连接云端所需的设备证书(ProductKey、DeviceName和DeviceSecret)后,您再使用MQTT客户端,将设备接入物联网平台,进行数据通信。
具体操作,请参见Paho-MQTT Java接入示例。